package com.huitone.smspfm.socket.client;

import java.io.IOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.apache.commons.codec.binary.Base64;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.bin.common.utils.DateTimeUtils;
import com.bin.common.utils.StringUtils;
import com.huitone.smspfm.socket.core.handler.AbstractMessageHandler;
import com.huitone.smspfm.socket.core.net.ExtSocket;
import com.huitone.smspfm.socket.protocol.sgip.constant.CommandID;
import com.huitone.smspfm.socket.protocol.sgip.constant.ErrorCode;
import com.huitone.smspfm.socket.protocol.sgip.message.Message;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.ActiveTest;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Bind;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.BindResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.IBody;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Submit;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Submit2;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Bind.LoginType;
import com.huitone.smspfm.socket.protocol.sgip.message.msghead.Head;
import com.huitone.smspfm.socket.protocol.sgip.message.msghead.IHead;
import com.huitone.smspfm.socket.utils.SerialUtils;

/**   
* @Title SmsClient.java 
* @Package com.huitone.smspfm.socket.client 
* @Description 短信客户端用于发送短信
* @author ybliang   
* @date 2017年10月12日 下午4:38:08 
* @version V1.0   
*/
public class SmsClient {
	
	private static final Logger logger = LogManager.getLogger(SmsClient.class);

	private String serverIp = "127.0.0.1";
	private Integer serverPort = 8901;
	private String loginName = null;
	private String loginPass = null;
	private String srvNode = null;
	private Integer codeRule = null;
	
	private ExtSocket socket = null;
	
	private AbstractMessageHandler messageHandler = null;
	
	private LoginType loginType = LoginType.CLIENT_TO_PLATFORM;
	
	private long lastTime = 0;
	
	private int lastCount = 0;
	
	private long maxSize = 0;
	
	private long lastTestTime = 0;
	
	private long testInterval = 10000;
	
	private boolean connectable = true;
	
	/**
	 * 是否将Handler和SmsClient客户端对象绑定在一起(Handler的生命周期和SmsClient的生命周期一致)
	 */
	private boolean isHandlerBound = false;
	
	
	/**
	 * 销毁客户端
	 */
	public synchronized void destory() {
		if (socket != null && !socket.isClosed()) {
			try {
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (isHandlerBound) {
			if (messageHandler != null) {
				messageHandler.destroy();
			}
		}
	}
	
	/**
	 * 
	 * @param interval true 如果在间隔时间内已经测试过则使用旧的测试结果; false则马上进行连通性测试,此时的结果是实时的
	 * @return
	 */
	public synchronized boolean testConnectableNow(boolean interval) {
		
		long currentTimeMillis = System.currentTimeMillis();
		
		if (interval) {
			lastTestTime = currentTimeMillis;
			connectable = testConnectable();
		} else {
			long tmpTimeInterval = currentTimeMillis - lastTestTime;
			if (tmpTimeInterval > this.testInterval) {
				lastTestTime = currentTimeMillis;
				connectable = testConnectable();
			}
		}
		
		return connectable;
	}
	
	/**
	 * 测试接口是否可连接
	 * @return
	 */
	public synchronized boolean testConnectable() {
		
		ActiveTest activeTest = new ActiveTest();
		
		Head head = Head.build(activeTest.length(), CommandID.SGIP_ACTIVE_TEST, codeRule, 
				StringUtils.toIntegerP(DateTimeUtils.dateToStr(new Date(), "MMddHHmmss")), SerialUtils.getSerial());
		
		boolean status = sendMsg(head, activeTest);
		
		return status;
	}
	
	public synchronized boolean testBindable() {
		return bind();
	}
	
	/**
	 * 绑定Socket连接
	 * @return
	 */
	public synchronized boolean bind() {
		
		if (socket != null && !socket.isClosed()) return true;
		
		if (loginName == null)
			throw new RuntimeException("用户名不能为NULL");
		
		if (loginPass == null) 
			throw new RuntimeException("用户密码不能为NULL");
		
		if (srvNode == null) 
			throw new RuntimeException("业务端编号不能为NULL");
		
		if (srvNode == null) 
			throw new RuntimeException("业务端编号规则不能为NULL");
		
		try {
			socket = new ExtSocket(serverIp, serverPort);
			socket.setKeepAlive(true);
			socket.setSendBufferSize(128 * 1024);
			socket.setReceiveBufferSize(128 * 1024);
		} catch (UnknownHostException e) {
			logger.error("创建Socket套接字出错,连接失败,目标IP[" + serverIp + "],目标端口[" + serverPort + "],错误信息:" + e.getMessage());
			closeSocket(); return false;
		} catch (IOException e) {
			logger.error("创建Socket套接字出错,连接失败,目标IP[" + serverIp + "],目标端口[" + serverPort + "],错误信息:" + e.getMessage());
			closeSocket(); return false;
		}
		
		Bind bind = Bind.build(srvNode, loginType.getValue(), loginName, StringUtils.trim(Base64.encodeBase64String(StringUtils.toString(loginPass).getBytes())));
		Head head = Head.build(bind.length(), CommandID.SGIP_BIND, codeRule, 
				StringUtils.toIntegerP(DateTimeUtils.dateToStr(new Date(), "MMddHHmmss")), SerialUtils.getSerial());
		
		Message bindMessage = new Message(head, bind);
		
		try {
			socket.write(bindMessage);
		} catch (IOException e) {
			logger.error("发送绑定消息出错,消息内容:" + bindMessage.toString() + ",错误信息:" + e.getMessage());
			closeSocket(); return false;
		}
		
		try {
			Message message = socket.read();
			IBody body = message.getBody();
			if (body != null && body instanceof BindResp) {
				BindResp bindResp = (BindResp) body;
				int result = bindResp.getResult();
				if (result == ErrorCode.CODE0) {
					logger.info("绑定成功,目标IP[" + serverIp + "],目标端口[" + serverPort + "]");
					if (messageHandler != null) {
						socket.setHandler(messageHandler);
					}
					socket.setReadCyclic(true);
					return true;
				} else {
					logger.warn("绑定失败,目标IP[" + serverIp + "],目标端口[" + serverPort + "], 错误信息:" + ErrorCode.codeDesc(result));
					closeSocket(); return false;
				}
			} else {
				logger.warn("绑定失败,目标IP[" + serverIp + "],目标端口[" + serverPort + "], 错误信息:应返回BindResp响应,实际返回了" + body.getClass().getName() + "响应!");
				closeSocket(); return false;
			}
		} catch (IOException e) {
			logger.error("读取绑定消息响应出错,错误信息:" + e.getMessage());
			e.printStackTrace();
			closeSocket(); return false;
		}
	}
	
	private void closeSocket() {
		if (socket != null && !socket.isClosed()) {
			try {
				socket.close();
			} catch (IOException e) {
				logger.error(e.getMessage(),e);
			}
		}
	}
	
	public synchronized boolean sendMsg(IHead head, IBody body) {
		return sendMsg(new Message(head, body));
	}
	
	public synchronized boolean sendMsg(Message message) {
		
		boolean sendstatus = false;
		int resendCount = 0;
		
		if (message == null) {
			logger.warn("Message参数为空!");
			return sendstatus;
		}
		
		do {
			
			boolean binded = bind();
			
			if (binded) {
				try {
					
					socket.write(message);
					
					// 发送速度控制
					speedController(message);
					
					sendstatus = true;
				} catch (IOException e) {
					if (e instanceof SocketException) {
						if (!socket.isClosed()) {
							try {
								socket.close();
								socket = null;
								resendCount ++;
							} catch (IOException ie) {
								ie.printStackTrace();
							}
						}
					} else {
						logger.error("Socket写消息出错,错误信息:" + e.getMessage());
					}
				}
			} else {
				resendCount ++;
			}
			
		} while (!sendstatus && resendCount < 1);
		
		return sendstatus;
	}
	
	private void speedController(Message message) {
		
		IBody body = message.getBody();
		
		if (body instanceof Submit || body instanceof Submit2) {
			
			long currentTimeMillis = System.currentTimeMillis();
			
			long interval = currentTimeMillis - lastTime;
			
			if (lastTime == 0) lastTime = currentTimeMillis;
			
			lastCount ++;
			
			int tmpCount = lastCount;
			
			if (maxSize != 0 && lastCount >= maxSize) {
				
				long sleep = 1000 - interval;
				
				if (sleep > 0) {
					try {
						TimeUnit.MILLISECONDS.sleep(sleep);
						lastCount = 0;
						lastTime = System.currentTimeMillis();
					} catch (InterruptedException e) {}
				}
				
				logger.warn("当前发送速率:" + tmpCount + "条/每秒!");
				
			} else {
				
				if (interval >= 1000) {
					lastCount = 0;
					lastTime = System.currentTimeMillis();
					logger.warn("当前发送速率:" + tmpCount + "条/每秒!");
				}
			}
		}
	}
	
	public String getServerIp() {
		return serverIp;
	}
	/**
	 * 设置目标服务器IP地址
	 * @param serverIp
	 * @return 
	 */
	public SmsClient setServerIp(String serverIp) {
		this.serverIp = serverIp;
		return this;
	}
	public Integer getServerPort() {
		return serverPort;
	}
	/**
	 * 设置目标服务器端口
	 * @param serverPort
	 * @return 
	 */
	public SmsClient setServerPort(Integer serverPort) {
		this.serverPort = serverPort;
		return this;
	}
	public String getLoginName() {
		return loginName;
	}
	/**
	 * 设置用户名
	 * @param loginName
	 * @return 
	 */
	public SmsClient setLoginName(String loginName) {
		this.loginName = loginName;
		return this;
	}
	public String getLoginPass() {
		return loginPass;
	}
	/**
	 * 设置用户密码
	 * @param loginPass
	 * @return 
	 */
	public SmsClient setLoginPass(String loginPass) {
		this.loginPass = loginPass;
		return this;
	}
	public String getSrvNode() {
		return srvNode;
	}
	
	/**
	 * 设置业务端编码
	 * @param srvNode
	 * @return 
	 */
	public SmsClient setSrvNode(String srvNode) {
		this.srvNode = srvNode;
		if (codeRule == null) {
			Integer integer = StringUtils.toInteger(srvNode, 0, true);
			if (integer != 0) {
				codeRule = integer;
			}
		}
		return this;
	}
	public Integer getCodeRule() {
		return codeRule;
	}
	
	/**
	 * 设置业务端编码规则<br>
	 * 当业务端编码规则codeRule和业务端编码srvNode相同时可以不手动设置
	 * @param codeRule
	 * @return 
	 */
	public SmsClient setCodeRule(Integer codeRule) {
		this.codeRule = codeRule;
		return this;
	}

	public AbstractMessageHandler getMessageHandler() {
		return messageHandler;
	}

	public void setMessageHandler(AbstractMessageHandler messageHandler) {
		this.messageHandler = messageHandler;
	}

	public ExtSocket getSocket() {
		return socket;
	}

	public void setSocket(ExtSocket socket) {
		this.socket = socket;
	}
	
	public long getLastReadTimeMillis() {
		if (socket != null) {
			return socket.getLastReadTimeMillis();
		}
		return 0;
	}

	public long getMaxSize() {
		return maxSize;
	}

	/**
	 * 设置客户端每秒钟最多可发送多少条短信<br>
	 * 注: maxSize = 0 时表示不做控制
	 * @param maxSize 
	 */
	public void setMaxSize(long maxSize) {
		this.maxSize = maxSize;
	}

	public long getTestInterval() {
		return testInterval;
	}

	public void setTestInterval(long testInterval) {
		this.testInterval = testInterval;
	}

	public boolean isConnectable() {
		return connectable;
	}

	public void setConnectable(boolean connectable) {
		this.connectable = connectable;
	}

	public boolean isHandlerBound() {
		return isHandlerBound;
	}

	/**
	 * 是否将Handler和SmsClient客户端对象绑定在一起(Handler的生命周期和SmsClient的生命周期一致)<br>
	 * true 是, false 否
	 */
	public void setHandlerBound(boolean isHandlerBound) {
		this.isHandlerBound = isHandlerBound;
	}
}
